package com.david.videoapp.hlsfunc;

import android.util.Log;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/***
 * 用于下载m3u8的视频，可用于android及pc及后台
 */
public class HlsDownloader {
//    public static void main(String[] args) {
//        String originUrlpath = "http://devimages.apple.com/iphone/samples/bipbop/gear1/prog_index.m3u8";
////        String originUrlpath = "https://aliyun.vod.pptv.com/f29837abded69519e53c405af454b505.m3u8?fpp.ver=1.0.0&k=ac009fe3f2db45e97e3e3a1c81bb89b7-ab2e-1622533995%26segment%3D3844ced7_38459f57_1622519595&vvid=e3392e70-4104-3b36-00c7-c5b43bf0c036&type=mpptv&o=m.pptv.com&sv=4.1.18";
////        String originUrlpath = "http://d2zihajmogu5jn.cloudfront.net/bipbop-advanced/bipbop_16x9_variant.m3u8";
//        String preUrlPath = originUrlpath.substring(0, originUrlpath.lastIndexOf("/")+1);
//        String rootPath = "/Users/mac/Documents/AndroidCode/HttpTest/videodir";
//        String fileName = "";
//        HlsDownloader downLoader = new HlsDownloader(originUrlpath, preUrlPath, rootPath, "");
//        //downLoader.setThreadQuantity(10);
//        try{
//            fileName = downLoader.download(true);
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//
//        if(fileName.isEmpty()){
//            System.out.println("下载失败");
//        }else{
//            System.err.println("下载成功");
//        }
//    }



    private String uuid;
    /**原始m3u8路径*/
    private String originUrlPath;
    /**.m3u8前的host跟path*/
    private String preUrlPath;
    /**存放文件的路径*/
    private String rootPath;
    /**合并成mp4后的文件名*/
    private String fileName;
    private String folderPath;
    private String pageTitle;
    private int threadQuantity = 10;
    private int tsSize = 0;

    public int getThreadQuantity() {
        return threadQuantity;
    }

    public void setThreadQuantity(int threadQuantity) {
        this.threadQuantity = threadQuantity;
    }


    public HlsDownloader(String originUrlPath, String preUrlPath, String rootPath, String pageTitle){
        setParams(originUrlPath, preUrlPath, rootPath, pageTitle);
    }

    public void setParams(String originUrlPath, String preUrlPath, String rootPath, String pageTitle){
        this.uuid = UUID.randomUUID().toString().replaceAll("-","");
        this.originUrlPath = originUrlPath;
        this.preUrlPath = preUrlPath;
        this.rootPath = rootPath;
        this.pageTitle = pageTitle;

        this.fileName = uuid+ ".mp4";

        this.folderPath = rootPath + File.separator + uuid;
        File file = new File(folderPath);
        if(!file.exists()) file.mkdirs();
    }

    /**下载方法*/
    public String download(boolean isAsync) throws Exception {
        List urlList = getIndexFileAndTss();
        tsSize = urlList.size();
        HashMap<Integer,String> keyFileMap = new HashMap<>();

        if (onDownLoadListener != null) {
            onDownLoadListener.start(tsSize);
        }

        if(isAsync){
            downLoadIndexFileAsync(urlList, keyFileMap);

            while (keyFileMap.size()<urlList.size()){
                //System.out.println("当前下载数量"+keyFileMap.size());
                Thread.sleep(3000);
            }
        }else{
            keyFileMap = downLoadIndexFile(urlList);
        }

        if (onDownLoadListener != null) {
            onDownLoadListener.onFinish(tsSize);
        }
//        return composeFile(keyFileMap);
//        return urlList.size() == new File(uuid).listFiles().length;
        return uuid + "";
    }

    /**获取ts列表及保存m3u8本地文件*/
    public List<String> getIndexFileAndTss() throws Exception{
        List<String> tss = new ArrayList<>();

        URL url = new URL(originUrlPath);
        //下载资源
        BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream(),"UTF-8"));

        String outName =  uuid + ".m3u8";
//        String outName =  pageTitle + ".m3u8";
//        String outName =  originUrlPath.substring(originUrlPath.lastIndexOf("/"));
//        if (outName.contains("?")) {
//            outName = outName.substring(0, outName.indexOf("?"));
//        }

        BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(rootPath + "/" + outName)));

        String line;
        int i = 0;
        while ((line = in.readLine()) != null) {
            Log.e("line", line);

            String tempFileName = line;
            if (line.contains("http") || line.contains(".ts")) {
                tempFileName = "./" +uuid + "/" + i + ".ts";
                ++i;

                tss.add(line);
            }
            out.write(tempFileName + "\n");
            out.flush();

        }
        out.close();
        in.close();

        return tss;
    }

    /**下载ts文件：同步*/
    public HashMap downLoadIndexFile(List<String> urlList){
        HashMap<Integer,String> keyFileMap = new HashMap<>();

        for(int i =0;i<urlList.size();i++){
            String subUrlPath = urlList.get(i);
            String fileOutPath = folderPath + File.separator + i + ".ts";
            keyFileMap.put(i, fileOutPath);
            try{
                downloadNet(preUrlPath + subUrlPath, fileOutPath);

                System.out.println("成功："+ (i + 1) +"/" + urlList.size());
            }catch (Exception e){
                System.err.println("失败："+ (i + 1) +"/" + urlList.size());
            }
        }

        return  keyFileMap;
    }


    private ExecutorService executorService = new ThreadPoolExecutor(3, 5,
            0L, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<>());

    /**下载ts文件：异步*/
    private void downLoadIndexFileAsync(List<String> urlList, HashMap<Integer, String> keyFileMap) {
        int downloadForEveryThread = (urlList.size() + threadQuantity - 1)/threadQuantity;
        if(downloadForEveryThread == 0) downloadForEveryThread = urlList.size();

        for(int i=0; i<urlList.size();i+=downloadForEveryThread){
            int startIndex = i;
            int endIndex = i + downloadForEveryThread - 1;
            if(endIndex >= urlList.size())
                endIndex = urlList.size() - 1;




            executorService.execute(new DownloadThread(urlList, startIndex, endIndex, keyFileMap));
        }
    }
    /**
     * 视频片段合成mp4
     */
    public String composeFile(HashMap<Integer,String> keyFileMap) throws Exception{

        if(keyFileMap.isEmpty()) return null;

        String fileOutPath = rootPath + File.separator + fileName;
        FileOutputStream fileOutputStream = new FileOutputStream(new File(fileOutPath));
        byte[] bytes = new byte[1024];
        int length = 0;
        for(int i=0; i<keyFileMap.size(); i++){
            String nodePath = keyFileMap.get(i);
            File file = new File(nodePath);
            if(!file.exists())  continue;

            FileInputStream fis = new FileInputStream(file);
            while ((length = fis.read(bytes)) != -1) {
                fileOutputStream.write(bytes, 0, length);
            }
        }

        return fileName;
    }

    /**实际下载的Runnable*/
    class DownloadThread implements Runnable{
        private List<String> urlList;
        private int startIndex;
        private int endIndex;
        private volatile HashMap<Integer,String> keyFileMap;

        public DownloadThread(List<String> urlList, int startIndex, int endIndex, HashMap<Integer,String> keyFileMap){
            this.urlList = urlList;
            this.startIndex = startIndex;
            this.endIndex = endIndex;
            this.keyFileMap = keyFileMap;
        }
        @Override
        public void run(){
            for(int i=startIndex;i<=endIndex;i++){
                String subUrlPath = urlList.get(i);
                String fileOutPath = folderPath + File.separator + i + ".ts";
                keyFileMap.put(i, fileOutPath);
                String message = "%s: 线程 " + (startIndex/(endIndex - startIndex) + 1)
                        + ", "+ (i + 1) +"/" + urlList.size() +", 合计: %d";



                System.out.println("url is " + (subUrlPath.startsWith("http")?subUrlPath : (preUrlPath + subUrlPath)));
                try{
//                    ts文件可能是全路径
                    downloadNet(subUrlPath.startsWith("http")?subUrlPath : (preUrlPath + subUrlPath), fileOutPath);

                    System.out.println(String.format(message, "成功", keyFileMap.size()));
                    if (onDownLoadListener != null) {
                        onDownLoadListener.onDownLoading(tsSize, keyFileMap.size());
                    }
                }catch (Exception e){
                    System.err.println(String.format(message, "失败", keyFileMap.size()));
                }
            }
        }
    }

    /**实际下载逻辑*/
    private void downloadNet(String fullUrlPath, String fileOutPath) throws Exception {
        // 下载网络文件
        int byteRead;

        URL url = new URL(fullUrlPath);
        URLConnection conn = url.openConnection();
        InputStream inStream = conn.getInputStream();
        FileOutputStream fs = new FileOutputStream(fileOutPath);

        byte[] buffer = new byte[1204];
        while ((byteRead = inStream.read(buffer)) != -1) {
            //bytesum += byteread;
            fs.write(buffer, 0, byteRead);
        }
    }

    private volatile OnDownLoadListener onDownLoadListener;

    public void setOnDownLoadListener(OnDownLoadListener onDownLoadListener) {
        this.onDownLoadListener = onDownLoadListener;
    }

    public interface OnDownLoadListener{
        void start(int allSize);
        void onDownLoading(int allSize, int downSize);
        void onFinish(int allSize);
    }
}